package com.gopay.bis.web.controller.expay;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.gopay.remote.cashier.bis.CashierBgNotifyRemoteService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.web.servlet.ModelAndView;

import com.gopay.bis.common.util.BisPayCheckUtils;
import com.gopay.bis.common.util.IpAddrUtil;
import com.gopay.bis.common.util.SendSmsUtils;
import com.gopay.bis.domain.order.OrderInfo;
import com.gopay.bis.service.bank.BankOrderService;
import com.gopay.common.Result;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.domain.bank.BankOrder;
import com.gopay.common.domain.bank.ExPayBaseMessage;
import com.gopay.common.domain.bank.external.BisExternalMessage;
import com.gopay.common.domain.bank.nucc.expay.NuccExpayHeaderMessage;
import com.gopay.common.domain.bank.nucc.expay.common.NuccExpayCommonMsgbody;
import com.gopay.common.domain.bank.nucc.expay.common.NuccExpayCommonMsgbodySysrtninf;
import com.gopay.common.domain.bank.nucc.expay.common.NuccExpayCommonRes;
import com.gopay.common.domain.proccode.ProcCodeInfo;
import com.gopay.common.proccode.DicProcCodeService;
import com.gopay.common.util.DateUtils;
import com.gopay.common.util.HttpTransport;
import com.gopay.common.util.RSAEncoderUtil;
import com.gopay.remote.bank.BankInteractRemoteService;
import com.gopay.remote.bank.ExPayInteractRemoteService;
import com.gopay.remote.bank.businesses.ExPayService;
import com.gopay.remote.bank.businesses.SerialApplyService;
import com.thoughtworks.xstream.XStream;

/**
 * 快捷支付返回处理基类
 * 
 * @author zhengxue
 * 
 */
public abstract class ExPayBankResBase {
    
    private final static String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
	
	private static Log logger = LogFactory.getLog(ExPayBankResBase.class);

	@Resource(name = "bisConfig")
	protected Map<String, String> bisConfig;
	
    @Resource(name = "nuccConfig")
    private Map<String, String> nuccConfig;

	@Resource(name = "exPaybankInteractService")
	private ExPayInteractRemoteService exPaybankInteractService;

	@Resource(name = "bisInteractExternalService")
	BankInteractRemoteService bisInteractExternalService;
	
	@Resource(name = "dicProcCodeService")
	private DicProcCodeService dicProcCodeService;

	@Resource(name = "notifyCashier")
	private HttpTransport notifyCashier;

	@Resource(name = "cashierBgNotifyRemoteService")
	private CashierBgNotifyRemoteService cashierBgNotifyRemoteService;
	
	@Resource(name = "bankOrderService")
	private BankOrderService bankOrderService;
	
	@Resource(name = "smsConfig")
    private Map<String, String> smsConfig;
	
    @Resource(name="nuccExPayServiceClient")
    private ExPayService nuccExPayServiceClient;
	/**
	 * 构建支付类对象
	 * 
	 * @param orderId
	 * @return
	 */
	protected abstract ExPayBaseMessage buildMessage(HttpServletRequest request);

	/**
	 * 构建返回收银台参数
	 * 
	 * @param request
	 * @return
	 */
	protected abstract OrderInfo buildOrderInfo(HttpServletRequest request,Result result);

	/**
	 * 验证返回参数
	 * 
	 * @param request
	 * @return
	 */
	protected abstract boolean checkParameters(HttpServletRequest request);

	/**
	 * 通知入口
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	protected ModelAndView execute(HttpServletRequest request,HttpServletResponse response) {
		// 打印请求参数
		this.displayRequestParameters(request, logger);
		//校验参数
		boolean isCheck = checkParameters(request);
		// 前台通知地址
		String notifyUrl = bisConfig.get("BIS.CLIENT.NOTIFY.CASHIER.BANK.RES");
		String viewName = bisConfig.get("BIS.NOTIFY.CASHIER.FORWARD.FAIL");
		ModelAndView model = new ModelAndView();
		model.setViewName(viewName);
		try {
			if(isCheck){
				OrderInfo orderInfo = null;
				// 验签
				Result result = exPaybankInteractService.verify(buildMessage(request));
				// 构建发往收银台的参数
				orderInfo = buildOrderInfo(request, result);
				orderInfo.setSign(sign(orderInfo));//  前台通知cashier签名数据
				logger.error(orderInfo.getOrderId() + " Referer: " + request.getHeader("Referer") + " Ip: " + IpAddrUtil.getRemoteRealIpAddr(request));
				if (result.isSuccess()) {
					if ((Boolean) result.getDefaultModel()) {
						Map<String, String> param  = (Map<String, String>)result.getModel("isNotifyCashier");
						param.put("sign", signRes(param));// 后台通知cashier签名数据
						String gopayOrderID = param.get("orderId");
						List bankOrderList = bankOrderService.getBankOrdersByGopayOrderId(gopayOrderID);
						 BankOrder bankOrder =null;
						 if(bankOrderList!=null && bankOrderList.size()>0){
							 bankOrder = (BankOrder)bankOrderList.get(0);
						 }
						//根据订单号判断如果为外部平台的话走外部平台返回签名流程
						if(BisPayCheckUtils.checkExternalOrderID(gopayOrderID)){
							BisExternalMessage bisMessage = new BisExternalMessage();
							bisMessage.setOrderId(gopayOrderID);
							bisMessage.setAmount(new BigDecimal(param.get("amt")));
							bisMessage.setBank(bankOrder.getBankCode());
							if(param.get("payStatus").equals("00")){
								bisMessage.setTradeState("1");
							}else{
								bisMessage.setTradeState("-1");
							}
							Result bisResult  = bisInteractExternalService.bankBack(bisMessage);
							//如果银行返回成功并且签名成功设置成功页面
							if(param.get("payStatus").equals("00") && bisResult.isSuccess()){
								String redirectUrl = (String)bisResult.getModel("redirectUrl");
								viewName = "redirect:"+redirectUrl;
							}
							model.setViewName(viewName);
							logger.error("外部平台订单["+gopayOrderID+"]处理成功，跳转地址： "+viewName);
							logger.error(param);
							return model;
						}else{
							//通知cashier入账
							String isFront = param.get("isFront");
							String payStatus = param.get("payStatus"); // 交易状态
							if(isFront.equals("false")){
							    if (!("00".equals(payStatus))) {
                                    // 交易状态非成功不通知cashier
                                    logger.error(gopayOrderID + " bis expay fail notifyCashier");
                                    /*notifyCashier.submit(param);*/ //生产优化10期 lx 2017-9-30 16:23:37
									cashierBgNotifyRemoteService.callBgNotify(param);
                                    viewName = bisConfig.get("BIS.DEAL.FORWARD");
                                } else {
                                    logger.error("bis expay notifyCashier: " + param);
                                    /*notifyCashier.submit(param);*/ //生产优化10期 lx 2017-9-30 16:23:37
									cashierBgNotifyRemoteService.callBgNotify(param);
                                    // 设置成功页面
                                    viewName = bisConfig.get("BIS.NOTIFY.CASHIER.FORWARD");
                                }
							}							
							
							// 通知银行
							if (orderInfo.getIsNotify()) {
								notifyBank(response, orderInfo);
							}							
						}
					} else if (!(Boolean) result.getDefaultModel()) {
						// 验签失败
						ProcCodeInfo procCodeInfo = dicProcCodeService.getProcCodeInfo(ProcCodeConstants.PROC_CODE_100E6040);
						orderInfo.setRetCode(ProcCodeConstants.PROC_CODE_100E6079);
						orderInfo.setRetMsg(procCodeInfo.getMessage());
						logger.error("平台订单["+orderInfo.getOrderId()+"]验签失败，跳转地址： "+viewName);
					}
					// TODO 执行不到下面流程
					else if (StringUtils.equals(ProcCodeConstants.PROC_CODE_100E6026,result.getResultCode())) {
						// 重复验签也返回成功页面，由收银台统一跳转
						viewName = bisConfig.get("BIS.NOTIFY.CASHIER.FORWARD");
						// 通知银行
						if (orderInfo.getIsNotify()) {
							notifyBank(response, orderInfo);
						}
					}
				} else {
					// 验签异常
					ProcCodeInfo procCodeInfo = dicProcCodeService.getProcCodeInfo(result.getResultCode());
					orderInfo.setRetCode(result.getResultCode());
					orderInfo.setRetMsg(procCodeInfo.getMessage());
					if(StringUtils.equals(result.getResultCode(), ProcCodeConstants.PROC_CODE_100E5079)){
                        SendSmsUtils.sendSmsErr(smsConfig.get("switch"), smsConfig.get("mobile"),"注意!!!订单金额不一致,银行:"+orderInfo.getBankName()+"orderId:"+orderInfo.getOrderId());
                    }
				}
				model.setViewName(viewName);
				model.addObject("notifyUrl", notifyUrl);
				model.addObject("orderInfo", orderInfo);
				model.addObject("errorCode", orderInfo.getRetCode());
				model.addObject("errorMsg", orderInfo.getRetMsg());
				logger.error(orderInfo);
				logger.error(notifyUrl);
				logger.error("平台订单["+orderInfo.getOrderId()+"]处理成功，跳转地址： "+viewName);
			}else{
				model.addObject("errorCode", ProcCodeConstants.PROC_CODE_100E7023);
				model.addObject("errorMsg", "bank back notify parameters is null");
			}
		} catch (Exception e) {
			logger.error("ExPayBankResBase error：", e);
			model.addObject("errorCode", ProcCodeConstants.PROC_CODE_100F1002);
			model.addObject("errorMsg", e.getMessage());
		}
		return model;
	}

	/**
	 * 通知银行处理结果
	 * 
	 * @param HttpServletResponse
	 * @param OrderInfo
	 * @return boolean
	 */
	protected boolean notifyBank(HttpServletResponse response,OrderInfo orderInfo) {
		StringBuffer paramters = new StringBuffer();
		paramters.append((String) bisConfig.get("BANK.PAY.CALLBACK.URL")).append("?");
		paramters.append("orderId=").append(orderInfo.getOrderId());
		paramters.append("&payStatus=").append(orderInfo.getPayStatus());
		paramters.append("&amt=").append(orderInfo.getAmt());
		paramters.append("&cardType=").append(orderInfo.getCardType());
		paramters.append("&retCode=").append(orderInfo.getRetCode());
		paramters.append("&bankName=").append(orderInfo.getBankName());
		String notifyMsg = paramters.toString();
		ServletOutputStream out = null;
		try {
			if ("umpay".equals(orderInfo.getBankName())) {
				notifyMsg = orderInfo.getSpecial();
			}
			if ("yspay".equals(orderInfo.getBankName())) {
				notifyMsg = new String(Base64.encode("0000".getBytes("GBK")));
			}
			if("nuccpay".equals(orderInfo.getBankName())){
			    NuccExpayCommonRes res = new  NuccExpayCommonRes();
                NuccExpayHeaderMessage header = new NuccExpayHeaderMessage();
                NuccExpayCommonMsgbody body = new NuccExpayCommonMsgbody();
                header.setSndDt(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss").replace(" ", "T"));
                //报文类型代码
                header.setMsgTp(nuccConfig.get("NUCC.EXPAY.COMMON.MSGTP"));
                //发起方所属机构标示
                header.setIssrId("");
                //报文方向  11-支付机构到平台
                header.setDrctn(nuccConfig.get("NUCC.EXPAY.DRCTN"));
                //签名证书序列号
                header.setSignSN(nuccConfig.get("NUCC.EXPAY.SIGNSN"));
                
                NuccExpayCommonMsgbodySysrtninf sysinf = new NuccExpayCommonMsgbodySysrtninf();
                sysinf.setSysRtnCd("000000");
                sysinf.setSysRtnTm(DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss").replace(" ", "T"));
                body.setSysRtnInf(sysinf);
                res.setMsgHeader(header);
                res.setMsgBody(body);
                res =(NuccExpayCommonRes) nuccExPayServiceClient.sign(res);
                String resMsg = res.getPlainMessage()+res.getSignMessage();
                response.addHeader("HTTP/1.1 200", "OK");
                response.addHeader("Content-Length",String.valueOf(resMsg.length()));
                response.addHeader("Content-type", "application/xml;charset=utf-8");
                response.setContentLength(resMsg.length());
                response.getWriter().write(resMsg);
                response.getWriter().flush();
                return true;
			}
			response.setContentType("text/html;charset=GBK");
			logger.error("notifyBank " + orderInfo.getBankName()+ " send content: \n[" + notifyMsg + "]");
			out = response.getOutputStream();
			out.print(notifyMsg);
			out.flush();
			return true;
		} catch (IOException e) {
			logger.error("notifyBank" + orderInfo.getBankName()+ "send error: ", e);
			return false;
		} catch(Exception ex){
		    logger.error("notifyBank" + orderInfo.getBankName()+ "send error: ", ex);
            return false;
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					logger.error("notifyBank close error: ", e);
					return false;
				}
			}
		}
	}
	
	public String sign(OrderInfo orderInfo) {
        StringBuffer sign = new StringBuffer();
        String orderId = orderInfo.getOrderId();
        String amt = orderInfo.getAmt();
        String retCode = orderInfo.getRetCode();
        String retMsg = orderInfo.getRetMsg();
        String payStatus = orderInfo.getPayStatus();
        String notifyNum = orderInfo.getNotifyNum();
        String cardType = orderInfo.getCardType();
        sign.append(orderId).append("|");
        sign.append(amt).append("|");
        sign.append(retCode).append("|");
        sign.append(retMsg).append("|");
        sign.append(payStatus).append("|");
        sign.append(notifyNum).append("|");
        sign.append(cardType);
        try {
            return RSAEncoderUtil.sign(sign.toString());
        } catch (Exception e) {
            logger.error("bis通知cashier OrderInfo签名异常", e);
        }
        return "";
    }
    
    public String signRes(Map res) {
        StringBuffer sign = new StringBuffer();
        String orderId = (String)res.get("orderId");
        String amt = (String)res.get("amt");
        String retCode = (String)res.get("retCode");
        String retMsg = (String)res.get("retMsg");
        String payStatus = (String)res.get("payStatus");
        String notifyNum = (String)res.get("notifyNum");
        String cardType = (String)res.get("cardType");
        sign.append(orderId).append("|");
        sign.append(amt).append("|");
        sign.append(retCode).append("|");
        sign.append(retMsg).append("|");
        sign.append(payStatus).append("|");
        sign.append(notifyNum).append("|");
        sign.append(cardType);
        try {
            return RSAEncoderUtil.sign(sign.toString());
        } catch (Exception e) {
            logger.error("bis通知cashier Map签名异常", e);
        }
        return "";
    }
	
	/**
	 * 输出请求参数
	 * 
	 * @param request
	 */
	@SuppressWarnings("rawtypes")
	protected void displayRequestParameters(HttpServletRequest request,Log logger) {
		for (Enumeration enumeration = request.getParameterNames(); enumeration.hasMoreElements();) {
			String eleName = (String) enumeration.nextElement();
			String eleValue = request.getParameter(eleName);
			if (logger != null) {
				logger.error("map key: [" + eleName + "]，value: [" + eleValue+ "]");
			}
		}
	}

}
